package com.lxk.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.UUID;
@Aspect
@Component
@Slf4j
public class TraceAspect {

    private static final String TRACE_ID = "traceId";

    private static final String LABEL = "label";

    @Around("@within(com.lxk.aspect.Trace) || @annotation(com.lxk.aspect.Trace)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature sign = (MethodSignature) joinPoint.getSignature();
        Method method = sign.getMethod();
        Trace trace = AnnotationUtils.findAnnotation(method, Trace.class);
        if (trace == null) {
            trace = AnnotationUtils
                    .findAnnotation(joinPoint.getTarget().getClass(), Trace.class);
        }
        String traceId = MDC.get(TRACE_ID);
        //用于标识是否是从切片这里产生的.如果是，则需要自己清理;否则，应该交由产生者进行清理(谁产生谁负责)
        boolean genFlag = false;
        //traceId不存在或者强制自动生成
        if(StringUtils.isEmpty(traceId)||trace.autoGen()){
            traceId = UUID.randomUUID().toString().replace("-","");
            MDC.put(TRACE_ID,traceId);
            genFlag = true;
        }
        MDC.put(LABEL,trace.label());
        Object result = null;
        Throwable error = null;
        try{
            result = joinPoint.proceed();
        }catch (Exception e){
            error = e;
        }
        //当traceId存在且是由切面产生的
        if(!StringUtils.isEmpty(MDC.get(TRACE_ID))&&genFlag){
            MDC.remove(TRACE_ID);
        }
        if(!StringUtils.isEmpty(MDC.get(LABEL))){
            MDC.remove(LABEL);
        }
        if(!ObjectUtils.isEmpty(error)){
            throw error;
        }
        return result;
    }

}
